#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QMessageBox>
#include <QFile>
#include <QFileDialog>

#include <QTextCodec>
#include <QDateTime>

#include "sundry.h"

bool MainWindow::nativeEvent(const QByteArray& eventType, void* message, qintptr* result)
{
    // 1. 转换消息类型
    MSG* msg = static_cast<MSG*>(message);

    // 2. 检查是否是设备变化消息
    if (msg->message == WM_DEVICECHANGE)
    {
        // 3. 获取设备广播头
        PDEV_BROADCAST_HDR pHdr = (PDEV_BROADCAST_HDR)msg->lParam;

        // 4. 根据事件类型处理
        switch (msg->wParam)
        {
        case DBT_DEVICEARRIVAL: // 设备插入
            qDebug() << "有设备插入";
            if (pHdr && pHdr->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
            {
                // handleDeviceAdded(pHdr);
            }
            break;

        case DBT_DEVICEREMOVECOMPLETE: // 设备移除
            qDebug() << "有设备移除";
            if (pHdr && pHdr->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
            {
                // handleDeviceRemoved(pHdr);
            }
            break;

        case DBT_DEVNODES_CHANGED: // 设备节点变化
            qDebug() << "设备节点发生变化";
            this->allPortInfo = QSerialPortInfo::availablePorts();//更新可用串口信息
            this->refreshComboBox_Port();//刷新下拉框的选项
            int index = findPortInfoIndex(this->portInfo, this->allPortInfo);
            if(index==-1)//没有找到
            {
                qDebug()<<portInfo.portName()<<portInfo.description()<<"被移除";
                if(serialPort->isOpen())
                {
                    this->closeSerialPort();
                }
                int newIndex = ui->comboBox_Port->currentIndex();
                portInfo = allPortInfo.at(newIndex);//同步选择的
                qDebug()<<"已选择"<<portInfo.portName()<<"序号"<<newIndex;
            }
            else
            {
                //将下拉框设置成之前选择的
                ui->comboBox_Port->setCurrentIndex(index);
                qDebug()<<"选择不变"<<portInfo.portName()<<"新序号"<<index;
            }
            break;
        }

        *result = TRUE; // 表示消息已处理
        return true;
    }

    // 其他消息交给 Qt 默认处理
    return QMainWindow::nativeEvent(eventType, message, result);
}

MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    this->serialPort = new QSerialPort(this);//串口对象

    this->debounceTimer=new QTimer(this);//防抖定时器
    debounceTimer->setSingleShot(true);//只触发一次
    debounceTimer->setInterval(500);// 500ms 防抖延迟

    localEncoding = getLocalEncoding(); // 获取本机编码

    // this->registerForDeviceNotifications(); // 设备通知注册

    // 根据串口是否打开设置相关组件
    connect(this, &MainWindow::portStateChange, this, &MainWindow::setSomeComponents);
    this->setSomeComponents(false);

    /*主窗口*/
    // 添加状态栏
    label_localEncoding = new QLabel("本地编码  " + getEncodingString(localEncoding));
    this->statusBar()->addWidget(label_localEncoding);
    label_portInfo=new QLabel("欢迎使用");
    this->statusBar()->addWidget(label_portInfo);



    /*串口配置*/
    // 显示串口列表
    this->allPortInfo = QSerialPortInfo::availablePorts();//获取所用可用串口信息
    this->refreshComboBox_Port();//添加至下拉框的列表中
    connect(ui->comboBox_Port,&QComboBox::activated, this, [this](int index){
        this->portInfo = this->allPortInfo.at(index);//更新选择的串口信息
        qDebug()<<"选择  "<<portInfo.portName();
    });
    int portIndex = ui->comboBox_Port->currentIndex();
    this->portInfo=this->allPortInfo[portIndex];
    qDebug()<<"选择  "<<portInfo.portName();

    // 添加标准波特率
    ui->comboBox_BaudRate->clear();
    for (const auto baud : QSerialPortInfo::standardBaudRates())
    {
        ui->comboBox_BaudRate->addItem(QString::number(baud), baud);
    }
    int baudRateIndex = ui->comboBox_BaudRate->findData(115200); // 默认115200波特率
    ui->comboBox_BaudRate->setCurrentIndex(baudRateIndex);
    // 添加数据位选项
    const QSerialPort::DataBits allDataBits[] = { QSerialPort::Data5, QSerialPort::Data6, QSerialPort::Data7, QSerialPort::Data8 };
    for (QSerialPort::DataBits dataBits : allDataBits)
    {
        ui->comboBox_DataBits->addItem(QString::number((int)dataBits), dataBits);
    }
    int dataBitsIndex = ui->comboBox_DataBits->findData(QSerialPort::Data8);
    ui->comboBox_DataBits->setCurrentIndex(dataBitsIndex); // 默认数据位8
    // 添加停止位选项
    ui->comboBox_StopBits->addItem("1", QSerialPort::StopBits::OneStop);
    ui->comboBox_StopBits->addItem("2", QSerialPort::StopBits::TwoStop);
    ui->comboBox_StopBits->setCurrentIndex(0); // 默认停止位1
    // 添加校验位选项
    ui->comboBox_Parity->addItem("无", QSerialPort::Parity::NoParity);
    ui->comboBox_Parity->addItem("奇", QSerialPort::Parity::OddParity);
    ui->comboBox_Parity->addItem("偶", QSerialPort::Parity::EvenParity);
    ui->comboBox_Parity->setCurrentIndex(0); // 校验位，默认为无
    // “开关”按钮
    connect(ui->pushButton_Switch, &QPushButton::clicked, this, &MainWindow::switchToggle);

    /*接收区设置窗口*/
    // 添加编码
    ui->comboBox_ReceiveEncoding->addItem("UTF-8", EncodingEnum::UTF8);
    ui->comboBox_ReceiveEncoding->addItem("GB18030", EncodingEnum::GB18030);
    if (localEncoding == EncodingEnum::GB18030)
    {
        int index = ui->comboBox_ReceiveEncoding->findData(EncodingEnum::GB18030);
        ui->comboBox_ReceiveEncoding->setCurrentIndex(index);
    }
    else
    {
        int index = ui->comboBox_ReceiveEncoding->findData(EncodingEnum::UTF8);
        ui->comboBox_ReceiveEncoding->setCurrentIndex(index);
    }
    // 清空接收
    connect(ui->pushButton_ReceivingClear, &QPushButton::clicked, this, [this]() {
        ui->plainTextEdit_Receive->clear();
        this->receiveBuffer.clear();
    });
    // 保存文件
    connect(ui->pushButton_SaveToFile, &QPushButton::clicked, this, &MainWindow::saveReceivedDataToFile);

    /*接收区窗口*/
    // 接收数据
    connect(serialPort, &QSerialPort::readyRead, this, &MainWindow::receiveData);

    /*发送区设置*/
    ui->comboBox_SendEncoding->addItem("UTF-8", EncodingEnum::UTF8);
    ui->comboBox_SendEncoding->addItem("GB18030", EncodingEnum::GB18030);
    if (localEncoding == EncodingEnum::GB18030)
    {
        int index = ui->comboBox_SendEncoding->findData(EncodingEnum::GB18030);
        ui->comboBox_SendEncoding->setCurrentIndex(index);
    }
    else
    {
        int index = ui->comboBox_SendEncoding->findData(EncodingEnum::UTF8);
        ui->comboBox_SendEncoding->setCurrentIndex(index);
    }
    // 添加发送选项
    ui->comboBox_SendOption->addItem("单项发送", SendOptions::singleItem);
    ui->comboBox_SendOption->addItem("多项发送", SendOptions::multipleItem);
    int optionIndex = ui->comboBox_SendOption->findData(SendOptions::singleItem); // 默认单项发送
    ui->comboBox_SendOption->setCurrentIndex(optionIndex);
    // 根据发送选项切换发送区是单项发送还是多项发送
    connect(ui->comboBox_SendOption, &QComboBox::currentIndexChanged, this, &MainWindow::sendAreaChange);

    /*数据发送区*/
    // 单项发送
    singleArea = new SendSingleArea(this);                                                       // 创建单项发送区
    connect(singleArea, &SendSingleArea::requestToSend, this, &MainWindow::sendData);            // 发送数据
    connect(this, &MainWindow::portStateChange, singleArea, &SendSingleArea::setSomeComponents); // 改变组件状态
    singleArea->setSomeComponents(false);
    //链接自动发送
    connect(this,&MainWindow::singleAutoSend,singleArea,&SendSingleArea::setAutoSendState);

    // 多项发送
    multipleArea = new SendMultipleArea(this);                                                       // 创建多项发送区
    this->multipleArea->hide();                                                                      // 默认隐藏
    connect(multipleArea, &SendMultipleArea::requestToSend, this, &MainWindow::sendData);            // 发送数据
    connect(this, &MainWindow::portStateChange, multipleArea, &SendMultipleArea::setSomeComponents); // 改变组件状态
    multipleArea->setSomeComponents(false);
    QGridLayout* gridLayout = new QGridLayout;
    gridLayout->addWidget(singleArea);
    gridLayout->addWidget(multipleArea);
    ui->groupBox_SendingArea->setLayout(gridLayout); // 设置布局
    //链接自动发送
    connect(this,&MainWindow::multipleAutoSend,multipleArea,&SendMultipleArea::setAutoSendState);

    //自动发送
    connect(ui->checkBox_AutoSend,&QCheckBox::clicked,this,[this](bool checked){
        if(checked)
        {
            int cycle=ui->spinBox_AutoSendCycle->value();
            if(!this->singleArea->isHidden())
            {
                emit singleAutoSend(true,cycle);
            }
            else if(!this->multipleArea->isHidden())
            {
                emit multipleAutoSend(true,cycle);
            }
        }
        else
        {
            emit multipleAutoSend(false, 32768);
            emit singleAutoSend(false, 32768);
        }
    });
    //改变周期
    connect(ui->spinBox_AutoSendCycle,&QSpinBox::valueChanged,this,[this](){
        //防抖
        this->debounceTimer->stop();// 先停止旧的
        this->debounceTimer->start(500);// 重新开始计时

    });
    //防抖定时器触发，读取数据
    connect(debounceTimer,&QTimer::timeout,[this](){
        // 当定时器最终触发时，获取当前稳定值
        int cycle=ui->spinBox_AutoSendCycle->value();
        if(!this->singleArea->isHidden())
        {
            emit singleAutoSend(true,cycle);
        }
        else if(!this->multipleArea->isHidden())
        {
            emit multipleAutoSend(true,cycle);
        }
    });
}

MainWindow::~MainWindow()
{
    if (serialPort->isOpen())
    {
        serialPort->close(); // 关闭串口
    }

    delete ui;
}

void MainWindow::openSerialPort()
{
    serialPort->setPort(portInfo); // 设置使用哪个串口

    // 设置串口通信参数
    int baudRate = ui->comboBox_BaudRate->currentData().toInt();
    serialPort->setBaudRate(baudRate); // 设置波特率
    QSerialPort::DataBits dataBits = static_cast<QSerialPort::DataBits>(ui->comboBox_DataBits->currentData().toInt());
    serialPort->setDataBits(dataBits); // 设置数据位
    QSerialPort::StopBits stopBits = static_cast<QSerialPort::StopBits>(ui->comboBox_StopBits->currentData().toInt());
    serialPort->setStopBits(stopBits); // 设置停止位
    QSerialPort::Parity parity = static_cast<QSerialPort::Parity>(ui->comboBox_Parity->currentData().toInt());
    serialPort->setParity(parity); // 设置校验位

    // 打开串口
    bool result = serialPort->open(QIODeviceBase::ReadWrite);
    if (result == false)
    {
        QMessageBox::warning(this, "错误", "打开串口时出现错误");
        qDebug() << "打开串口"<<portInfo.portName()<<"时出现错误";
        return;
    }
    else
    {
        qDebug() << "成功打开串口"<<portInfo.portName();
        label_portInfo->setText(portInfo.portName()+ "已打开  "+portInfo.description());
        // QMessageBox::information(this, "提示信息", "串口已经被成功打开");
        emit portStateChange(true);
    }
}

void MainWindow::closeSerialPort()
{
    serialPort->close(); // 关闭串口
    qDebug()<<"串口已关闭";
    emit portStateChange(false);
}

void MainWindow::registerForDeviceNotifications()
{
    // 1. 创建设备接口结构体
    DEV_BROADCAST_DEVICEINTERFACE dbh = { 0 };
    dbh.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
    dbh.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
    // 注意：这里我们不指定具体的设备类，接收所有设备接口的通知

    // 2. 注册设备通知
    HDEVNOTIFY hDevNotify = RegisterDeviceNotification((HWND)winId(), // 接收消息的窗口句柄
        &dbh,                                                         // 设备接口结构体
        DEVICE_NOTIFY_WINDOW_HANDLE |                                 // 使用窗口句柄
            DEVICE_NOTIFY_ALL_INTERFACE_CLASSES                       // 接收所有设备类的通知
    );

    if (!hDevNotify)
    {
        qDebug() << "设备通知注册失败，错误码:" << GetLastError();
    }
    else
    {
        qDebug() << "设备通知注册成功";
    }
}

void MainWindow::switchToggle()
{
    if (!serialPort->isOpen())/*打开串口*/
    {
        openSerialPort();
    }
    else/*关闭串口*/
    {
        closeSerialPort();
        label_portInfo->setText(portInfo.portName()+ "已关闭  ");
    }
}

void MainWindow::sendAreaChange(int index)
{
    SendOptions option = static_cast<SendOptions>(ui->comboBox_SendOption->itemData(index).toInt());
    if (option == multipleItem)
    {
        this->singleArea->hide();
        this->multipleArea->show();
        emit this->singleAutoSend(false, 32768);//关闭单项发送区的自动发送
        if(ui->checkBox_AutoSend->isChecked())
        {
            int cycle=ui->spinBox_AutoSendCycle->value();
            emit this->multipleAutoSend(true, cycle);//开启多项发送区的自动发送
        }
    }
    else if (option == singleItem)
    {
        this->singleArea->show();
        this->multipleArea->hide();
        emit this->multipleAutoSend(false,32768);//关闭多项发送区的自动发送
        if(ui->checkBox_AutoSend->isChecked())
        {
            int cycle=ui->spinBox_AutoSendCycle->value();
            emit this->singleAutoSend(true,cycle);//开启单项发送区的自动发送
        }
    }
}

void MainWindow::refreshComboBox_Port()
{
    ui->comboBox_Port->clear();
    for (const auto& portInfo : this->allPortInfo)
    {
        QString item = portInfo.portName() + ":" + portInfo.description();
        ui->comboBox_Port->addItem(item);
    }
}

void MainWindow::saveReceivedDataToFile()
{
    // 定义文件过滤器：让用户选择 txt 或 bin
    QString filter = "Text File (*.txt);;Binary File (*.bin)";
    QString defaultSuffix = "txt"; // 默认使用 .txt

    // 弹出“另存为”对话框
    QString fileName = QFileDialog::getSaveFileName(this, // 父窗口
        "保存接收数据",                                   // 对话框标题
        "",                                               // 默认路径（空表示上次路径）
        filter,                                           // 文件类型过滤器
        nullptr,                                          // 用于接收选中的过滤器
        QFileDialog::Option::DontConfirmOverwrite         // 先不自动提示覆盖，我们自己处理
    );

    if (fileName.isEmpty()) // 如果用户取消了选择，fileName 为空
    {
        return;
    }

    // 如果用户没输入后缀，根据选择的过滤器添加默认后缀
    QFileInfo info(fileName);
    if (info.suffix().isEmpty())
    {
        fileName += ".txt"; // 默认添加 .txt
    }

    // 检查是否要覆盖文件
    QFile file(fileName);
    if (file.exists())
    {
        QMessageBox::StandardButton ret =
            QMessageBox::question(this, "覆盖文件", QString("文件 %1 已存在，是否覆盖？").arg(fileName), QMessageBox::Yes | QMessageBox::No);
        if (ret == QMessageBox::No)
        {
            return; // 用户取消覆盖
        }
    }

    // 尝试打开文件写入
    if (!file.open(QIODevice::WriteOnly))
    {
        QMessageBox::warning(this, "保存失败", "无法打开文件：" + file.errorString());
        return;
    }

    // 写入数据
    int64_t result = file.write(receiveBuffer);
    if (result == -1)
    {
        QMessageBox::warning(this, "保存失败", "写入文件时出错：" + file.errorString());
    }
    else
    {
        QMessageBox::information(this, "保存成功", QString("成功保存 %1 字节到:\n%2").arg(result).arg(fileName));
    }

    file.close();
}

void MainWindow::receiveData()
{
    /*读取数据*/
    QByteArray byteArray = serialPort->readAll();

    // 如果停止显示
    if (ui->checkBox_StopDisplaying->isChecked())
    {
        return;
    }

    /*保存到缓冲区*/
    this->receiveBuffer.append(byteArray);

    /*在文本框中显示*/
    QString stringShow = ""; // 要显示的字符串

    bool timestamp = ui->checkBox_Timestamp->isChecked();                                                   // 是否选择了时间戳
    bool text = ui->radioButton_ReceiveText->isChecked();                                                   // 是否选择了文本
    EncodingEnum encoding = static_cast<EncodingEnum>(ui->comboBox_ReceiveEncoding->currentData().toInt()); // 选择的编码

    // 如果选择了时间戳
    if (timestamp)
    {
        QDateTime now = QDateTime::currentDateTime();
        QString nowString = now.toString("\nhh:mm:ss.zzz");
        stringShow += (nowString + "  >>\n");
    }

    if (text) // 如果选择了文本
    {
        QString dataString;
        QByteArray encodingName = getEncodingString(encoding);      // 得到编码的字符串
        QTextCodec* codec = QTextCodec::codecForName(encodingName); // 获取编码器
        dataString = codec->toUnicode(byteArray);                   // 解码为 QString
        stringShow += dataString;
    }
    else // 如果选择了HEX
    {
        QString hexString = byteArrayToHexSpaced(byteArray);
        stringShow += hexString;
    }

    ui->plainTextEdit_Receive->insertPlainText(stringShow);
}

void MainWindow::sendData(QString dataString)
{
    if(!this->serialPort->isOpen())
    {
        return;
    }

    bool text = ui->radioButton_SendText->isChecked();                                                   // 是否选择了文本
    EncodingEnum encoding = static_cast<EncodingEnum>(ui->comboBox_SendEncoding->currentData().toInt()); // 选择的编码
    QByteArray encodingName = getEncodingString(encoding);                                               // 得到编码的字符串
    QTextCodec* codec = QTextCodec::codecForName(encodingName);

    QByteArray dataSend;//要发送的数据

    if(text)//如果选择了文本
    {
        dataSend = codec->fromUnicode(dataString);
    }
    else//选择了 HEX
    {
        if(isValidHexSequence(dataString)!=true)
        {
            QMessageBox::warning(this,"警告","要发送的不是 HEX 字符串，请检查或者发送文本");
            qDebug()<<"要发送的不是 HEX 字符串，请检查或者发送文本";
            return;
        }
        dataSend = hexSpacedToByteArray(dataString);//将对应字符串还原为十六进制数据
    }


    // 在结尾追加
    if (ui->checkBox_Append->isChecked())
    {
        switch (ui->comboBox_Append->currentIndex())
        {
        case 0:
            dataSend += "\r\n";
            break;
        case 1:
            dataSend += '\n';
            break;
        case 2:
            dataSend += '\0';
            break;
        default:
            break;
        }
    }

    serialPort->write(dataSend); // 通过串口发送数据
}

void MainWindow::setSomeComponents(bool checked)
{
    ui->comboBox_Port->setEnabled(!checked);
    ui->comboBox_BaudRate->setEnabled(!checked);
    ui->comboBox_DataBits->setEnabled(!checked);
    ui->comboBox_StopBits->setEnabled(!checked);
    ui->comboBox_Parity->setEnabled(!checked);
    ui->checkBox_AutoSend->setEnabled(checked);//打开了才允许自动发送
    if (checked)//串口打开了
    {
        ui->pushButton_Switch->setText("关闭串口");
    }
    else//串口关闭了
    {
        ui->pushButton_Switch->setText("打开串口");
        ui->checkBox_AutoSend->setChecked(false);//取消勾选
        emit this->singleAutoSend(false,32768);//关闭自动发送
        emit this->multipleAutoSend(false, 32768);
    }
}
